/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package an_sintatico;

import an_lexico.AnLexico;
import an_semantico.AnSemantico;
import an_sintatico.AnSintatico;
import an_sintatico.TransSintatica;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.StringTokenizer;

/**
 *
 * @author A
 */
public class AnSintaticoFactory {

	/**
	 *
     */
    class tempInfoSintatico {

	    /**
	     *
         */
        public int col = 0;

        /**
		 *
         */
        public int lin = 0;

        /**
		 *
         */
        public int[] initialStates = null,  finalStates = null;

        /**
		 *
         */
        public TransSintatica transitionTable[][] = null;
    }

	/**
	 *
     */
    private PrintStream printTo;

	/**
	 *
     */
    AnSintatico sint;

	/**
	 *
     */
    AnLexico lexico;

	/**
	 *
     */
    AnSemantico semantico;

	/**
	 *
     */
    String subMachineIDsSint[];

	/**
	 *
     */
    private int lastSubMachine;

	/**
	 *
     */
    DataInputStream configFile;

	/**
	 *
     */
    public AnSintaticoFactory() {
        printTo = new PrintStream(new OutputStream() {

            @Override
            public void write(int b) throws IOException {
                return;
            }
        });
    }

	/**
	 *
     */
    public AnSintatico createAnSintatico(final AnLexico lex, final AnSemantico sem) {
        sint = new AnSintatico(lex, sem);
        lexico = lex;
        semantico = sem;

        ReadConfigFile();

        return sint;
    }

    /**
     * All the parameters of AnSintatico are initialized inn this method. There
     * should not be another method where these parameters are modified.
     */
    private void ReadConfigFile() {
        configFile = openConfigFile("./config/tt_sint.txt");

        try {
            tempInfoSintatico temp = new tempInfoSintatico();
            //Get the number of sub machines
            int numberOfSubMachines = getNumberOfSubMachines();
            subMachineIDsSint = new String[numberOfSubMachines];
            temp.initialStates = new int[numberOfSubMachines];

            //Get the size of the transition table
            temp = getSizeOfTransitionTable(temp);
            sint.setCol(temp.col);
            sint.setLin(temp.lin);

            temp.transitionTable = new TransSintatica[temp.col][temp.lin];
            for (int i = 0; i < temp.col; i++) {
                for (int j = 0; j < temp.lin; j++) {
                    temp.transitionTable[i][j] = new TransSintatica(-1, -1, -1);
                }
            }

            int[] tokenTypes = getTokenTypes(temp);
            sint.setTokenTypes(tokenTypes);

            temp.finalStates = new int[temp.lin];

            //Get the Transition Table, initialStates and finalStates
            lastSubMachine = -1;
            int lastTableLineRead = 0;
            for (int lastSubMachineRead = 0; lastSubMachineRead < numberOfSubMachines; lastSubMachineRead++) {
                lastTableLineRead = getSubMachine(lastTableLineRead, temp);
            }
            sint.setSubMachineInitialState(temp.initialStates);
            sint.setTransTable(temp.transitionTable);
            sint.setFinalStates(temp.finalStates);
            sint.setSubMachineID(subMachineIDsSint);
        } catch (IOException ex) {
            throw new Error(ex);
        }

    }

    /**
     *
     * @param configFile
     * @param firstSubMachineLine the first table line to be used by this sub
     * machine.
     * @return the last table line read from this subMachine
     * @throws java.io.IOException
     */
    @SuppressWarnings("deprecation")
    private int getSubMachine(int firstSubMachineLine, tempInfoSintatico temp) throws IOException {
        StringTokenizer st = new StringTokenizer(configFile.readLine());

        int numberOfSubMachineStates = Integer.parseInt(st.nextToken());
        printTo.println("numberOfSubMachineStates = " + numberOfSubMachineStates);

        int linesOfTransitions = Integer.parseInt(st.nextToken());
        printTo.println("linesOfTransitions = " + linesOfTransitions);

        //Descobre qual sub maquina estamos lendo.
        st = new StringTokenizer(configFile.readLine());
        String subMachineName = st.nextToken();
        int subMachine = getSubMachineNumberByID(subMachineName);
        printTo.println("subMachineName = " + subMachineName);
        if (subMachine == -1) {
            subMachine = newSubMachine(subMachineName);
        }
        printTo.println("getting subMachine = " + subMachine);
        st.nextToken();//String = ":"

        //Get the Transitions
        temp.initialStates[subMachine] = firstSubMachineLine;
        for (int i = 0; i < linesOfTransitions; i++) {
            getTransitionLine(temp, firstSubMachineLine, numberOfSubMachineStates);
        }

        int nextFirstSubMachineLine = firstSubMachineLine + numberOfSubMachineStates;
        printTo.println("lastLinePreviousSubMachine: " + firstSubMachineLine +
                "linSubMachine: " + numberOfSubMachineStates);
        return nextFirstSubMachineLine;
    }

 	/**
 	 *
     */
 	private void getTransitionLine(tempInfoSintatico temp, int firstSubMachineLine, int numberOfSubMachineStates) throws IOException {
        int semanticFunction = -1;
        int subMaquina = -1;
        int nextState = -1;
        int currentState = -1;
        @SuppressWarnings("deprecation")
        StringTokenizer st = new StringTokenizer(configFile.readLine());
        /* Ordem dos parametros a serem lidos do arquivo para transições:
         *   - Chamada de sub maquina(opcional)
         *   - Estado Atual
         *   - Seta (  -> )
         *   - Estado Destino
         *   - Tipo de Token que provoca a transicao
         *   - Função Semântica a ser executada quando a maquina realizar a
         *     transicao
         * Ordem dos parametros a serem lidos para propriedades do estado:
         *   - Propriedade
         *   - Estado
         */
        String start = st.nextToken();

        if (start.equals("final")) {
            int state = Integer.parseInt(st.nextToken());
            if (state >= numberOfSubMachineStates) {
                throw new Error("State does not exist in this sub machine");
            }
            temp.finalStates[state + firstSubMachineLine] = 1;
            return;
        }

        if (start.startsWith(".")) {
            //sub Machine Call
            subMaquina = getSubMachineNumberByID(start.substring(1));
            if (subMaquina == -1) {//Se a sub maquina ainda nao foi lida, aloca uma posicao para ela.

                subMaquina = newSubMachine(start.substring(1));
            }
            start = st.nextToken();
        }

        currentState = Integer.parseInt(start) + firstSubMachineLine;
        if (currentState - firstSubMachineLine >= numberOfSubMachineStates) {
            throw new Error("State does not exist in this sub machine");
        }
        st.nextToken();//->

        nextState = Integer.parseInt(st.nextToken()) + firstSubMachineLine;
        if (nextState - firstSubMachineLine >= numberOfSubMachineStates) {
            throw new Error("State does not exist in this sub machine");
        }
        st.nextToken(); // -

        String tokenID = st.nextToken();
        int collumn = sint.getTokenTypeCollumn(lexico.getTokenTypeByID(tokenID));
        st.nextToken();//-

        String sf = st.nextToken();//Semantic Function
        try {
            semanticFunction = Integer.parseInt(sf);
            System.out.println("aeaeae" + semanticFunction);
        } catch ( NumberFormatException nfe) {
        //OK
        }

        temp.transitionTable[collumn][currentState] = new TransSintatica(nextState, semanticFunction, subMaquina);
        return;
    }


 	/**
 	 *
     */
 	private int newSubMachine(String subMachineName) {
        lastSubMachine++;
        printTo.println("new subMachineName = " + subMachineName);
        subMachineIDsSint[lastSubMachine] = subMachineName;
        return lastSubMachine;
    }

 	/**
 	 * Extract submachine number by ID
 	 * @param subMachineName
 	 * @return submachine number
     */
 	private int getSubMachineNumberByID(String subMachineName) {
        int i = 0;
        for (String id : subMachineIDsSint) {
            if (subMachineName.equals(id)) {
                return i;
            }
            i++;
        }
        return -1;
    }

	/**
	 * Get token types
	 *
	 * @param tempInfoSintatico
	 * @return token type
     */
    private int[] getTokenTypes(tempInfoSintatico temp) throws IOException {
        int[] types = new int[temp.col];
        @SuppressWarnings("deprecation")
        StringTokenizer st = new StringTokenizer(configFile.readLine());
        for ( int i = 0; i < temp.col; i++) {
            String id = st.nextToken();
            types[i] = lexico.getTokenTypeByID(id);
            printTo.println(i + " " + types[i] + " " + id);
        }
        return types;
    }

	/**
	 * Get size of the transition table
	 *
	 * @param tempInfoSintatico
	 * @return size
     */
    private tempInfoSintatico getSizeOfTransitionTable(tempInfoSintatico t) throws IOException {
        @SuppressWarnings("deprecation")
        StringTokenizer st = new StringTokenizer(configFile.readLine());
        t.col = Integer.parseInt(st.nextToken());
        t.lin = Integer.parseInt(st.nextToken());
        return t;
    }

	/**
	 * Get number of submachines
	 *
	 * @return number
     */
    private int getNumberOfSubMachines() throws IOException {
        @SuppressWarnings("deprecation")
        StringTokenizer st = new StringTokenizer(configFile.readLine());
        return Integer.parseInt(st.nextToken());
    }

	/**
	 * Open configuration file
	 *
	 * @param File path
     */
    private DataInputStream openConfigFile(String path) {
        File f = new File(path);
        try {
            FileInputStream fis = new FileInputStream(f);
            DataInputStream d = new DataInputStream(fis);
            return d;
        } catch (FileNotFoundException fnf) {
            throw new Error("Erro na abertura do arquivo!! - ConfigFile.");
        }
    }
}
